﻿using System;
using System.Collections.Generic;
using System.Linq;
using DTO.Reports;
using IMModel.Domain;
using IMModel.Domain.Common;
using DTO;
using Characteristic = DTO.Characteristic;
using Category = DTO.Category;
using Gradation = DTO.Gradation;
using Person = IMModel.Domain.Person;
using Term = DTO.Term;


namespace DataAccessLayer
{
    using DataAccessLayer.Properties;

    /// <summary>
    /// Базовый класс для преобразования объектов Linq2SQL в DTO-объекты
    /// </summary>
    internal partial class DTOConverter
    {

        public static readonly DTOConverter Instance = new DTOConverter();

        #region Privates

        /// <summary>
        /// Создаем базовый DTO-объект TermBase
        /// </summary>
        /// <typeparam name="T">Тип, наследуемый от TermBase </typeparam>
        /// <param name="T">Linq2SQL-класс Term</param>
        /// <returns>DTO-объект TermBase</returns>
        private T CreateTerm<T>(IMModel.Domain.Term Term) where T : DTO.TermBase, new()
        {
            return new T
            {
                IDTerm = Term.IDTerm,
                Start = Term.Start,
                Stop = Term.Stop,
                StartFill = (DateTime)Term.StartFill,
                StopFill = (DateTime)Term.StopFill,
                IDOrganization = Term.IDOrganization??0,
                IDPeriod = Term.IDPeriod??0
            };
        }

        /// <summary>
        /// Создаем базовый DTO-объект
        /// </summary>
        /// <typeparam name="T">Тип, наследуемый от Base </typeparam>
        /// <param name="T">Linq2SQL-класс</param>
        /// <returns>DTO-объект</returns>
        private T CreateOrganization<T>(IMModel.Domain.Organization Term) 
            where T : DTO.OrganizationBase, new()
        {
            return new T
            {
                IDOrganization = Term.IDOrganization,
                Name = Term.Name,
                Abbriviation = Term.Abbriviation
            };
        }

        /// <summary>
        /// Создаем базовый DTO-объект
        /// </summary>
        /// <typeparam name="T">Тип, наследуемый от Base </typeparam>
        /// <param name="T">Linq2SQL-класс</param>
        /// <returns>DTO-объект</returns>
        private T CreatePeriod<T>(IMModel.Domain.Period Term)
            where T : DTO.PeriodBase, new()
        {
            return new T
            {
                IDPeriod = Term.IDPeriod,
                Start = Term.Start,
                Stop = Term.Stop
            };
        }

        /// <summary>
        /// Создает DTO-объект Respondent
        /// </summary>
        /// <param name="R">Класс, сгенерированный Linq2SQL</param>
        /// <returns>DTO-объект</returns>
        private T CreateResp<T, U, W>(IMModel.Domain.Respondent R)
            where T : RespondentBase, new()
            where U : PersonBase, new()
            where W : DepartmentBase, new()
        {
            return new T
                    {
                        IDRespondent = R.IDRespondent,
                        Person = CreatePerson<U>(R.Person),
                        Department = CreateDepartment<W>(R.DepartmentTerm, false),
                        State = StateObject.Unchanged
                    };
        }

        private T CreatePerson<T>(IMModel.Domain.Person P)
            where T : DTO.PersonBase, new()
        {
            return new T
            {
                FirstName = P.FirstName,
                LastName = P.LastName,
                Patronymic = P.Patronymic,
                IDPerson = P.IDPerson,
                State = StateObject.Unchanged,
                EMail = P.E_Mail
            };
        }

        private T CreateDepartment<T>(IMModel.Domain.DepartmentTerm D, bool LoadChildsDeps)
            where T : DepartmentBase, new()
        {
            return new T()
            {
                IDDepartment = D.IDDepartment,
                Name = D.Department.Name,
                Abbriviation = D.Department.Abbriviation,
                Parent = CreateParent<T>(D),
                Childs = LoadChildsDeps ? GetChilds<T>(D, LoadChildsDeps).ToList() : new List<DTOBase>()
            };
        }

        private T CreateCategory<T>(IMModel.Domain.CategoryTerm CT)
            where T : CategoryBase, new()
        {
            return new T
            {
                IDCategory = CT.Category.IDCategory,
                Name = CT.Category.Name,
                State = StateObject.Unchanged,
                Weight = CT.Weight
            };
        }

        private T CreateCharacteristic<T, U>(IMModel.Domain.CharacteristicTerm CharTerm)
            where T : DTO.CharacteristicBase, new()
            where U : DTO.CategoryBase, new()
        {
            T Char = new T
            {
                Category = this.CreateCategory<U>(CharTerm.CategorySectionTerm.CategoryTerm),
                Childs = null,
                ID = CharTerm.IDCharacteristicTerm,
                Name = CharTerm.Characteristic.Name,
                Order = CharTerm.Order,
                State = StateObject.Unchanged,
                Weight = CharTerm.Weight
            };
            return Char;
        }

        private IEnumerable<DTO.DTOBase> GetChilds<T>(IMModel.Domain.DepartmentTerm D, bool LoadChilds) where T : DTO.DepartmentBase, new()
        {
            var list = new List<DTO.DTOBase>();
            foreach (DepartmentTerm DT in D.DepartmentTerms)
            {
                list.Add(CreateDepartment<T>(DT, LoadChilds));
            }
            foreach (IMModel.Domain.Respondent R in D.Respondents)
            {
                list.Add(CreateResp(R));
            }
            return list;
        }

        private T CreateParent<T>(IMModel.Domain.DepartmentTerm D) where T : DTO.DepartmentBase, new()
        {
            if (D.DepartmentTerm1 != null)
            {
                return CreateDepartment<T>(D.DepartmentTerm1, false);
            }
            else
            {
                return null;
            }
        }

        private T CreateSection<T, U, W>(IMModel.Domain.SectionTerm S, bool LoadChilds)
            where T : DTO.SectionBase, new()
            where U : DTO.CharacteristicBase, new()
            where W : DTO.CategoryBase, new()
        {
            return new T
            {
                Name = S.Section.Name,
                Order = S.Order,
                Weight = S.Weight,
                ID = S.IDSectionTerm,
                Childs = LoadChilds ? GetChilds(S).ToList() : null
            };
        }

        private IEnumerable<DTO.IndexMarkItem> GetChilds<T, U, W>(IMModel.Domain.SectionTerm S, bool LoadChilds)
            where T : DTO.SectionBase, new()
            where U : DTO.CharacteristicBase, new()
            where W : DTO.CategoryBase, new()
        {
            List<DTO.IndexMarkItem> childs = new List<IndexMarkItem>();

            foreach (IMModel.Domain.SectionTerm ST in S.SectionTerms)
            {
                childs.Add(CreateSection<T, U, W>(ST, LoadChilds));
            }

            foreach (IMModel.Domain.CategorySectionTerm CatT in S.CategorySectionTerms)
            {
                foreach (IMModel.Domain.CharacteristicTerm CT in CatT.CharacteristicTerms)
                {
                    childs.Add(CreateCharacteristic<U, W>(CT));
                }
            }

            return childs.OrderBy(x => x.Order, new NatSort<String>()).ToList();
        }

        private IEnumerable<DTO.IndexMarkItem> GetChilds(IMModel.Domain.SectionTerm S)
        {
            List<DTO.IndexMarkItem> childs = new List<IndexMarkItem>();

            foreach (IMModel.Domain.SectionTerm ST in S.SectionTerms)
            {
                childs.Add(CreateSection(ST));
            }

            foreach (IMModel.Domain.CategorySectionTerm CatT in S.CategorySectionTerms)
            {
                foreach (IMModel.Domain.CharacteristicTerm CT in CatT.CharacteristicTerms)
                {
                    childs.Add(CreateCharacteristic(CT));
                }
            }

            return childs.OrderBy(x => x.Order, new NatSort<String>()).ToList();
        }

        private IEnumerable<DTO.Answer> CreateAnswers(IEnumerable<IMModel.Domain.Answer> answers)
        {
            return answers.Select(x => this.CreateAnswer(x)).ToList();
        }

        private T CreateGradationGroup<T>(GradationTerm gradationTerm) where T : DTO.GradationBase, new()
        {
            return new T
            {
                Name = gradationTerm.Gradation.Name,
                ID = gradationTerm.IDGradationTerm,
                Childs = CreateChilds<T>(gradationTerm).OrderBy(x => x.Order, new NatSort<String>()).ToList()
            };
        }

        private IEnumerable<T> CreateChilds<T>(GradationTerm gradationTerm) where T : DTO.GradationBase, new()
        {
            return gradationTerm.GradationTerms.Select(x => CreateGradation<T>(x));
        }

        private T CreateGradation<T>(GradationTerm GT) where T : DTO.GradationBase, new()
        {
            return new T
            {
                ID = GT.IDGradationTerm,
                Name = GT.Gradation.Name,
                Weight = (double)GT.Weight,
                Order = GT.Order,
                Group = GT.GradationTerm1.Gradation.Name,
                Childs = CreateChilds<T>(GT).ToList()
            };
        }

        #endregion

        /// <summary>
        /// Создает DTO-объект Answer
        /// </summary>
        /// <param name="A">Класс, сгенерированный Linq2SQL</param>
        /// <returns>DTO-объект</returns>
        public DTO.Answer CreateAnswer(IMModel.Domain.Answer A)
        {
            return new DTO.Answer
            {
                IDAnswer = A.IDAnswer,
                State = StateObject.Unchanged,
                Volume = (float?)A.Volume,
                IDCharTerm = A.IDCharacteristicTerm,
               // IDCharTerm = A.CharacteristicTerm.IDCharacteristicTerm,
                Gradations = this.CreateGradations<DTO.Gradation>(A.GradationsAnswers).ToList()
                //Resp = CreateResp(A.Respondent);
                
            };
        }

        private IEnumerable<DTO.Gradation> CreateGradations<T>(IEnumerable<GradationsAnswer> gradations) 
            where T : DTO.GradationBase, new()
        {
            return gradations.Select(x => this.CreateGradation(x));
        }

        /// <summary>
        /// Создает DTO-объект Gradation
        /// </summary>
        /// <param name="G">Класс, сгенерированный Linq2SQL</param>
        /// <returns>DTO-объект</returns>
        public DTO.Gradation CreateGradation(IMModel.Domain.GradationsAnswer GA)
        {
            return new DTO.Gradation
            {
                ID = GA.GradationTerm.IDGradationTerm,
                Name = GA.GradationTerm.Gradation.Name,
                Unit = (GA.GradationTerm.IDUnit!=null)?GA.GradationTerm.Unit.Name:"",
                Order = GA.GradationTerm.Order,
                State = StateObject.Unchanged,
                Volume = (float?)GA.Volume,
                Weight = GA.GradationTerm.Weight != null ? (double)GA.GradationTerm.Weight : 0,
                Childs = null,
                Group = GA.GradationTerm.GradationTerm1 != null ? 
                    GA.GradationTerm.GradationTerm1.Gradation.Name : String.Empty
            };
        }

        private String GetNormalizeWeight(int IDCharacteristicTerm)
        {
            using (IMModel.Domain.IMDataDataContext db = new IMModel.Domain.IMDataDataContext(Settings.Default.IndexMarkConnectionString))
            {
                IMModel.Domain.Common.IItem curr = db.CharacteristicTerms.Single(x => x.IDCharacteristicTerm == IDCharacteristicTerm);

                string s = string.Empty;

                while (curr.Parent != null)
                {
                    if (!string.IsNullOrEmpty(s)) s += " * ";
                    s += Math.Round(curr.GetNormalizedWeight(), 2);
                    curr = curr.Parent;
                }

                return s;
            }
        }

        private double GetNormalizeDoubleWeight(int IDCharacteristicTerm)
        {
            using (IMModel.Domain.IMDataDataContext db = new IMModel.Domain.IMDataDataContext(Settings.Default.IndexMarkConnectionString))
            {
                IMModel.Domain.Common.IItem curr = db.CharacteristicTerms.Single(x => x.IDCharacteristicTerm == IDCharacteristicTerm);

                double d = 1;

                while (curr.Parent != null)
                {
                    d *= Math.Round(curr.GetNormalizedWeight(), 2);
                    curr = curr.Parent;
                }

                return d;
            }
        }


        //private double GetMaxFromAnswer(int IDCharGrad)
        //{
        //    using (IMModel.Domain.IMDataDataContext db = new IMModel.Domain.IMDataDataContext())
        //    {
        //        return Convert.ToDouble(Math.Round((from A in db.Answers
        //                                            where A.IDCharGrad == IDCharGrad
        //                                            select A.Volume).Max(), 0));
        //    }
        //}

        #region InputMethods

        public DTO.Position CreatePosition(IMModel.Domain.Position Pos)
        {
            return new DTO.Position
                       {
                           Name = Pos.Name
                       };
        }

        public DTO.Term CreateTerm(IMModel.Domain.Term T)
        {
            DTO.Term Term = CreateTerm<DTO.Term>(T);
            return Term;
        }

        public DTO.Period CreatePeriod(IMModel.Domain.Period T)
        {
            DTO.Period Term = CreatePeriod<DTO.Period>(T);
            return Term;
        }

        public DTO.Organization CreateOrganization(IMModel.Domain.Organization T)
        {
            DTO.Organization Term = CreateOrganization<DTO.Organization>(T);
            return Term;
        }

        //public DTO.AdmTerm CreateAdmTerm(IMModel.Domain.Term T)
        //{
        //    DTO.AdmTerm AT = CreateTerm<DTO.AdmTerm>(T);
        //    AT.k1 = T.k1;
        //    AT.k2 = T.k2;

        //    return AT;
        //}

        public DTO.Characteristic CreateCharacteristic(IMModel.Domain.CharacteristicTerm CharTerm)
        {
            DTO.Characteristic Char = CreateCharacteristic<DTO.Characteristic, DTO.Category>(CharTerm);
            Char.Responsibles = CreateResponsibles(CharTerm).ToList();
            Char.Unit = CharTerm.Unit.Name;
            Char.VisibleVolums = CharTerm.Visibility;
            Char.IsGraduated = CharTerm.CharacteristicGroups.Count > 0;
            return Char;
        }

        private IEnumerable<string> CreateResponsibles(IMModel.Domain.CharacteristicTerm CharTerm)
        {
            return CharTerm.CharacteristicResponsibles.Select(x => x.Responsible.Position.Name).Distinct();
        }

        public DTO.Department CreateDepartmentWithNotRespondents(IMModel.Domain.DepartmentTerm D)
        {
            DTO.Department Dep = CreateDepartment<DTO.Department>(D, true);
            Dep.Type = D.Department.DepartmentType.Name;
            return Dep;
        }

        public DTO.Respondent CreateResp(IMModel.Domain.Respondent R)
        {
            DTO.Respondent Resp = CreateResp<DTO.Respondent, DTO.Person, DTO.Department>(R);
            Resp.Position = R.Job.Position.Name;
            Resp.Status = R.Job.Status.Order.ToString();
            return Resp;
        }

        public DTO.Section CreateSection(IMModel.Domain.SectionTerm ST)
        {
            DTO.Section Sec = CreateSection<DTO.Section, DTO.Characteristic, DTO.Category>(ST, true);
            return Sec;
        }

        public DTO.RespondentAnswers CreateRespAnswers(IMModel.Domain.Respondent R, int IDCharTerm)
        {
            return new RespondentAnswers
            {
                Resp = CreateResp(R),
                IDCharTerm = IDCharTerm,
                Answers = CreateAnswers(R.Answers.Where(x => x.IDCharacteristicTerm == IDCharTerm && x.Answers.Count == 0 && x.IsDeleted == false))
              
            };
        }

        public DTO.Gradation CreateGradationGroup(CharacteristicGroup CharGroup)
        {
            DTO.Gradation Grad = CreateGradationGroup<DTO.Gradation>(CharGroup.GradationTerm);
            Grad.Unit = CharGroup.GradationTerm.IDUnit != null ? CharGroup.GradationTerm.Unit.Name : String.Empty;
            Grad.IsNecessary = CharGroup.IsNecessary;
            return Grad;
        }

        #endregion

        //public DTO.IndexRow CreateIndexes(IMModel.Domain.Respondent R, double Index, DTO.IndexListType Type)
        //{
        //    return new IndexRow()
        //               {
        //                   FIO = R.Person.ShortName,
        //                   Position = R.Job.Position.Name,
        //                   PositionOrder = (int)R.Job.Position.Order,
        //                   ParentDepartment = R.DepartmentTerm.DepartmentTerm1.Department.Abbriviation,
        //                   IDRespondent = R.IDRespondent,
        //                   Department = R.Department.Abbriviation,
        //                   Index = RoundIndex(Index),
        //                   IndexHarmon = RoundIndex(R.HarmonyIndex),
        //                   IndexLider = new IndexLider()
        //                                    {
        //                                        StringView = RoundIndex(R.LeaderIndex).ToString() + R.LeaderIndexSections.OrderBy(x=>x.Order).Aggregate("", (x,y)=>(x + " \\ " + y.Order)),
        //                                        Index = RoundIndex(R.LeaderIndex)                                             
        //                                    },
        //                    SectionsIndex = Type == IndexListType.Compact ? new List<SectionIndex>(R.Term.TopmostParticipateSectionTerms.Select(x=>new SectionIndex()
        //                                                                                                               {
        //                                                                                                                   Index=RoundIndex(x.Indexes[R]),
        //                                                                                                                   Section = x.Order
        //                                                                                                               }).OrderBy(x=>x.Section)) 
        //                                                                                                               :
        //                                                                    new List<SectionIndex>(R.Term.SectionTerms.Select(x => new SectionIndex()
        //                                                                    {
        //                                                                        Index = RoundIndex(x.Indexes[R]),
        //                                                                        Section = x.Order
        //                                                                    }).OrderBy(x => x.Section))
        //               };
        //}


        public DTO.IndexRow CreateIndexesStub(IMModel.Domain.Respondent R, double Index, DTO.IndexListType Type)
        {
            
            return new IndexRow()
                       {
                           Resp = CreateResp(R),
                           Index = RoundIndex(Index),
                           IndexHarmony = RoundIndex(R.HarmonyIndex),
                           IndexLider =
                               new IndexLider(RoundIndex(R.LeaderIndex),
                                              R.LeaderIndexSections.OrderBy(x => x.Order).Aggregate("",
                                                                                                    (x, y) => (y.Order))),
                        
                           OrderField = ((R.Job.Position.Order) != null ? R.Job.Position.Order.Value : 0),
                           Position = R.Job.Position.Name,
                           SectionIndex = (Type == IndexListType.Compact) ? new List<SectionIndex>(R.Term.TopmostParticipateSectionTerms.Select(x => new SectionIndex()
                                                                                                                      {

                                                                                                                          Index = RoundIndex(x.Indexes[R]),
                                                                                                                          Section = x.Order
                                                                                                                      }).OrderBy(x => x.Section))
                                                                                                                      :
                                                                           new List<SectionIndex>(R.Term.SectionTerms.Select(x => new SectionIndex()
                                                                           {
                                                                               Index = RoundIndex(x.Indexes[R]),
                                                                               Section = x.Order
                                                                           }).OrderBy(x => x.Section))
                       };
                       

        }


        private double RoundIndex(double Index)
        {
            return Math.Round(Index * 1000, 2);
        }

        public static DTO.Reports.NullChar CreateNullChar(IMModel.Domain.CharacteristicTerm ct)
        {
            return new NullChar
            {
                Order = ct.Order,
                Name = ct.Characteristic.Name,
                Responsibles = string.Join(", ", ct.CharacteristicResponsibles.Select(x =>
                    x.Responsible.Position.Name))
            };
        }

        //public DTO.Money CreateMoney(IMModel.Domain.Respondent R)
        //{
        //    return new DTO.Money
        //    {
        //        Premium = Math.Round(R.Premium, 2),
        //        Resp = new DTO.Respondent
        //        {
        //            Name = R.Person.ShortName,
        //            Position = R.Job.Position.Name + " - " + R.Job.Status.Order + " ст.",
        //            Department = R.Department.Name,
        //            ParentDepartment = R.DepartmentTerm.DepartmentTerm1.Department.Name,
        //            RespID = R.IDRespondent
        //        }
        //    };
        //}

        public DTO.Category CreateCategory(IMModel.Domain.CategoryTerm x)
        {
            DTO.Category Cat = CreateCategory<DTO.Category>(x);
            return Cat;
        }

        public DTO.Department CreateDepartmentWithRespondents(DepartmentTerm x)
        {
            DTO.Department Dep = CreateDepartment<DTO.Department>(x, true);
            Dep.Type = x.Department.DepartmentType.Name;
            return Dep;
        }


        public DTO.Reports.VolumePPS CreateVolumePPS(IMModel.Domain.Answer A)
        {
            return new DTO.Reports.VolumePPS
            {
                Name = A.Respondent.Person.ToString(),
                Volume = A.Volume.ToString()
            };
        }

        public VolumsPPS CreateV(Characteristic grChar, IList<RespondentAnswers> answers, List<IMModel.Domain.Respondent> teahers, IMModel.Domain.Term termId, IMModel.Domain.Term CurrentTermId)
        {
            VolumsPPS VP = new VolumsPPS();
            //  VP.Gradation = gradation.Order+ " - " + gradation.Name;
            VP.Category = new Category
            {
                Name = grChar.Category.Name,
                Weight = grChar.Category.Weight

            };
            VP.Unit = grChar.Unit;
            //VP.Max = this.GetMaxFromAnswer(grChar.ID);
            VP.Volums = new List<VolumePPS>();
            VP.IDCharGrad = grChar.ID;
            VP.Char = grChar;
            // && x.IDCharTerm == grChar.ID

            foreach (var respondent in teahers)
            {
                VolumePPS t = new VolumePPS();
                //выбираем ответы респондета по характестике
                var rans = answers.Where(x => x.Resp.Person.IDPerson == respondent.Person.IDPerson).SelectMany(x => x.Answers);
                //ищем градации 
                t.Answers = rans.ToList();
                foreach (var answer in t.Answers)
                {
                    
                    answer.VolumeWithWeight = answer.Volume + " * " + GetNormalizeWeight(grChar.ID) + " = " + GetNormalizeDoubleWeight(grChar.ID) * answer.Volume;
                    if (termId.IDTerm != CurrentTermId.IDTerm) answer.VolumeWithWeight += "(получен в " + CurrentTermId.Start.ToShortDateString() + " - " + CurrentTermId.Stop.ToShortDateString() + " периоде)";

                }

                VP.Volums.Add(t);
            }

            return VP;
        }


        public VolumsPPS CreateV2(Characteristic grChar, IList<RespondentAnswers> answers, List<IMModel.Domain.Respondent> teahers, IMDataDataContext context)
        {
            VolumsPPS VP = new VolumsPPS();
            //  VP.Gradation = gradation.Order+ " - " + gradation.Name;
            VP.Category = new Category
            {
                Name = grChar.Category.Name,
                Weight = grChar.Category.Weight

            };
            VP.Unit = grChar.Unit;
            //VP.Max = this.GetMaxFromAnswer(grChar.ID);&& x.IDCharTerm == grChar.ID
            VP.Volums = new List<VolumePPS>();
            VP.IDCharGrad = grChar.ID;
            VP.Char = grChar;


            foreach (var respondent in teahers)
            {
                VolumePPS t = new VolumePPS();
                //выбираем ответы респондета по характестике
                var rans = answers.Where(x => x.Resp.Person.IDPerson == respondent.Person.IDPerson ).SelectMany(x => x.Answers);
                //ищем градации 
                t.Answers = rans.ToList();
                foreach (var answer in t.Answers)
                {
                    answer.VolumeWithWeight = answer.Volume + " * " + GetNormalizeWeight(grChar.ID) + " = " + GetNormalizeDoubleWeight(grChar.ID) * answer.Volume;
                }

                VP.Volums.Add(t);
            }

            return VP;
        }
    }
}
